package utils

import (
	"context"
	"fmt"
	"gin-luban-server/global"
	"gin-luban-server/model"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"golang.org/x/crypto/ssh"
	"net"
	"strconv"
	"strings"
)


// 声明一个全局的rdb变量
var rdb *redis.Client
var ctx = context.Background()


/*
说明: - 20211223
需要支持ssh tunnle代理，需要修改源代码
crypto包中ssh包中tcpip.go文件中的函数 SetReadDeadline、和 SetWriteDeadline 中
	return errors.New("ssh: tcpChan: deadline not supported")
	设置return nil
否则可能报错
*/

func getSSHClient(addr, user, pass, sshType, sshKey string ) (*ssh.Client, error) {
	fmt.Println(addr, user, pass, sshType, sshKey,  "------------")
	config := &ssh.ClientConfig{
		User:user,
		Timeout: 120,
		//需要验证服务端，不做验证返回nil，没有该参数会报错
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}
	switch sshType {
	case "password":
		config.Auth = []ssh.AuthMethod{ssh.Password(pass)}
	case "key":
		signer, _ := ssh.ParsePrivateKey([]byte(sshKey))
		config.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
	default:
		return nil, fmt.Errorf("unknow ssh auth type: %s", sshType)
	}

	sshConn, err := net.Dial("tcp", addr)

	if nil != err {
		fmt.Println("net dial err: ", err)
		return nil, err
	}

	clientConn, chans, reqs, err := ssh.NewClientConn(sshConn, addr, config)
	if nil != err {
		sshConn.Close()
		fmt.Println("ssh client conn err: ", err)
		return nil, err
	}
	client := ssh.NewClient(clientConn, chans, reqs)


	return client, nil
}

func GetRedisClient(redisAddr string, password string, sshProxy string, db int) (*redis.Client, error) {
	// 非直连使用ssh代理
	var redisCli *redis.Client

	//ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
	//defer cancel()
	//fmt.Println("-------------",redisAddr, password, sshProxy,db )
	if sshProxy != "" && sshProxy != "connect"{
		var sshConfigInfo model.SysBasicConfigure
		err := global.GVA_DB.Model(&model.SysBasicConfigure{}).Where("purpose = ? ",sshProxy).First(&sshConfigInfo).Error
		// 首先获取sshClient
		cli, err := getSSHClient(sshConfigInfo.ProxyHost+":"+sshConfigInfo.ProxyPort, sshConfigInfo.BasicUser, sshConfigInfo.BasicPasswd, sshConfigInfo.SshType, sshConfigInfo.SshKey)
		if nil != err {
			global.GVA_LOG.Error("get ssh client err: %v\n", zap.Any("error", err.Error()))
			return nil, err
		}
		// 借助ssh连接redis
		redisCli = redis.NewClient(&redis.Options{
			Network: "tcp", // 连接方式，默认使用tcp，可省略
			Addr:    redisAddr,
			Password: password,
			DB:      db, // 选择要操作的数据库，默认是0 （redis中select index命令）
			Dialer: func(ctx context.Context, network, addr string) (conn net.Conn, e error) {
				return cli.Dial(network, addr)
			},

		})
	} else {
		redisCli = redis.NewClient(&redis.Options{
			Network: "tcp", // 连接方式，默认使用tcp，可省略
			Addr:    redisAddr,
			Password: password,
			DB:      db, // 选择要操作的数据库，默认是0 （redis中select index命令）
		})
	}

	if err := redisCli.Ping(ctx).Err(); nil != err {
		global.GVA_LOG.Error("connect to redis err: ", zap.Any("error", err.Error()))

		return nil, err
	}

	return redisCli, nil
}

// 初始化连接 此函数已经废弃
/*func GetRedisInfo(node string, password string ) (err error, info string) {
	rdb = redis.NewClient(&redis.Options{
		Addr:     node,
		Password: password,
		DB:       0,  // use default DB
	})
	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err, info
	}


	info, err =  rdb.Info(ctx).Result()
	//fmt.Println("-------", info)
	return nil, info
}
*/



func FormatRedisInfo(info string, redisNodeInfo *model.RedisNodeInfo, redisNodeOtherInfo *model.RedisNodeOtherInfo) (err error) {
	var dbKeysArray []int
	infoArray := strings.Split(strings.Trim(info, "\n"), "\n")
	for _ ,v := range infoArray {
		// 统一个里面的所有元素都 除去 \r 字符
		v = strings.Trim(v, "\r")
		if strings.HasPrefix(v, "uptime_in_seconds") {
			redisNodeInfo.UptimeInSeconds = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "redis_version") {
			redisNodeInfo.RedisVersion = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "redis_mode") {
			redisNodeInfo.RedisMode = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "os") {
			redisNodeInfo.OS = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "used_memory:") {
			redisNodeInfo.UsedMemory = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "total_system_memory_human") {
			redisNodeInfo.TotalSystemMemoryHuman = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "role") {
			redisNodeInfo.NodeRole = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "db") {
			dbKeys, _ :=strconv.Atoi(strings.Split(strings.Split(strings.Split(v, ":")[1], ",")[0], "=")[1])
			dbKeysArray = append(dbKeysArray, dbKeys)
		}
		// 所有db 中的 keys总数求和
		redisNodeInfo.TotalKeys = Sum(dbKeysArray)
		redisNodeInfo.DBSize = len(dbKeysArray)


		// redisNodeOtherInfo 信息
		if strings.HasPrefix(v, "used_memory_peak:") {
			redisNodeOtherInfo.UsedMemoryPeak = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "used_memory_rss:") {
			redisNodeOtherInfo.UsedMemoryRss = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "connected_clients") {
			redisNodeOtherInfo.ConnectedClients = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "blocked_clients") {
			redisNodeOtherInfo.BlockedClients = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "expired_keys") {
			redisNodeOtherInfo.ExpiredKeys = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "total_commands_processed") {
			redisNodeOtherInfo.TotalCommandsProcessed = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "keyspace_hits") {
			redisNodeOtherInfo.KeySpaceHits = strings.Split(v, ":")[1]
		}
		if strings.HasPrefix(v, "keyspace_misses") {
			redisNodeOtherInfo.KeySpaceMisses = strings.Split(v, ":")[1]
		}
	}
	//fmt.Println("----------------",redisNodeInfo.NodeRole)
	//fmt.Println("----------------",redisNodeInfo.TotalSystemMemoryHuman)
	//fmt.Println("----------------",redisNodeInfo.UsedMemoryHuman)
	//fmt.Println("----------------",redisNodeInfo.OS)
	//fmt.Println("----------------",redisNodeInfo.TotalKeys)
	//fmt.Println("----------------",redisNodeInfo.DBSize)
	return  err
}

func GetDBList(rdb *redis.Client) (dbList []map[string]interface{}, err error) {
	dbInfo, err := rdb.Info(ctx, "Keyspace").Result()
	infoArray := strings.Split(strings.Trim(dbInfo, "\n"), "\n")
	for _,dbLine := range infoArray {
		if strings.HasPrefix(dbLine, "db") {
			dbList = append(dbList, map[string]interface{} {
				"database": strings.Split(dbLine, ":")[0],
				"keys": strings.Split(strings.Split(strings.Split(dbLine, ":")[1], ",")[0], "=")[1],
			})
		}
	}
	return dbList, err
}


// 命令
// 遍历 切片 求和
func Sum(arr []int) (result int) {
	for _, v := range arr {
		result += v
	}
	return result
}

